package com.onluy.frameworkcloud.batis.dao;


import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import org.apache.ibatis.exceptions.TooManyResultsException;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.defaults.DefaultSqlSession.StrictMap;
import org.mybatis.spring.SqlSessionTemplate;
import com.onluy.frameworkcloud.batis.bean.Batch;
import com.onluy.frameworkcloud.batis.bean.CacheBoundSql;
import com.onluy.frameworkcloud.batis.bean.EntityBoundSql;
import com.onluy.frameworkcloud.batis.dialect.Dialect;
import com.onluy.frameworkcloud.batis.exception.EmptyBatchListException;
import com.onluy.frameworkcloud.batis.exception.ExceedsMaxLimitResultsetException;
import com.onluy.frameworkcloud.batis.holder.BatchHolder;
import com.onluy.frameworkcloud.batis.holder.BoundSqlHolder;
import com.onluy.frameworkcloud.batis.holder.ResultMapHolder;
import com.onluy.frameworkcloud.batis.tools.util.EntityUtil;
import com.onluy.frameworkcloud.batis.tools.util.ResultMapUtil;
import com.onluy.frameworkcloud.batis.tools.util.SQlGenerator;
import com.onluy.frameworkcloud.data.bean.PageData;
import com.onluy.frameworkcloud.data.bean.Pagination;
import com.onluy.frameworkcloud.data.entity.BaseEntity;
import com.onluy.frameworkcloud.data.entity.Entity;
import com.onluy.frameworkcloud.data.entity.EntityHelper;




public class SqlSessionDao extends AbstractDao {
    private SqlSession sqlSession;
    private Dialect dialect;
    private int exportPageSize;
    private SQlGenerator sqlGeneration;

    public SqlSessionDao(SqlSessionFactory sqlSessionFactory, String dialect)
            throws InstantiationException, IllegalAccessException, ClassNotFoundException {
        this(sqlSessionFactory, dialect, 2000);
    }

    public SqlSessionDao(SqlSessionFactory sqlSessionFactory, String dialect, int exportPageSize)
            throws InstantiationException, IllegalAccessException, ClassNotFoundException {
        this.sqlSession = new SqlSessionTemplate(sqlSessionFactory);
        this.dialect = (Dialect) Class.forName(dialect).newInstance();
        this.exportPageSize = exportPageSize;
        this.sqlGeneration = new SQlGenerator(this.dialect);
    }

    public SqlSession getSqlSession() {
        return this.sqlSession;
    }

    public Dialect getDialect() {
        return this.dialect;
    }

    public <E> List<E> selectList(String statementId) {
        return this.selectList((String) statementId, (Object) null);
    }

    public <E> List<E> selectList(String statementId, Object param) {
        Object pagination = null;
        PageData pageData = this.selectList((String) statementId, (Object) param, (Pagination) pagination);
        return pageData.getRows();
    }

    public <E> PageData<E> selectList(String statementId, Object param, Pagination pagination) {
        MappedStatement ms = this.sqlSession.getConfiguration().getMappedStatement(statementId);
        return this.selectList(statementId, this.getBoundSql(ms, param), param, pagination, (Integer) null);
    }

    public <E> List<E> selectList(Entity entity) {
        PageData pageData = this.selectList((Entity) entity, (Pagination) null, (Integer) null);
        return pageData.getRows();
    }

    public <E> List<E> selectList(Entity entity, Integer cacheSeconds) {
        PageData pageData = this.selectList((Entity) entity, (Pagination) null, (Integer) cacheSeconds);
        return pageData.getRows();
    }

    public <E> PageData<E> selectList(Entity entity, Pagination pagination) {
        return this.selectList((Entity) entity, (Pagination) pagination, (Integer) null);
    }

    public <E> PageData<E> selectList(Entity entity, Pagination pagination, Integer cacheSeconds) {
        String statementId = "EntitySQL.select";
        MappedStatement ms = this.sqlSession.getConfiguration().getMappedStatement(statementId);
        BoundSql originalBoundSql = this.getBoundSql(ms, entity);
        EntityBoundSql entityBoundSql = this.sqlGeneration.generateSelectSql(ms.getConfiguration(), entity);
        MetaObject boundSqlMeta = SystemMetaObject.forObject(originalBoundSql);
        boundSqlMeta.setValue("sql", entityBoundSql.getSql());
        boundSqlMeta.setValue("parameterMappings", entityBoundSql.getParameterMappings());
        return this.selectList(statementId, originalBoundSql, entity, pagination, cacheSeconds);
    }

    public <T> T selectOne(String statementId) {
        return this.selectOne((String) statementId, (Object) null);
    }

    public <T> T selectOne(String statementId, Object param) {
        List list = this.selectList(statementId, param, RowBounds.DEFAULT);
        if (list.size() == 1) {
            return (T)list.get(0);
        } else if (list.size() > 1) {
            throw new TooManyResultsException(
                    "Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
        } else {
            return null;
        }
    }

    public <T> T selectOne(Entity entity) {
        return this.selectOne((Entity) entity, (Integer) null);
    }

    public <T> T selectOne(Entity entity, Integer cacheSeconds) {
        List list = this.selectList(entity, cacheSeconds);
        if (list.size() == 1) {
            return (T)list.get(0);
        } else if (list.size() > 1) {
            throw new TooManyResultsException(
                    "Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
        } else {
            return null;
        }
    }

    public Long selectCount(String statementId) {
        return this.selectCount((String) statementId, (Object) null);
    }

    public Long selectCount(String statementId, Object param) {
        MappedStatement ms = this.sqlSession.getConfiguration().getMappedStatement(statementId);
        BoundSql boundSql = this.getBoundSql(ms, param);
        return this.selectCount(statementId, boundSql, param, false, (Integer) null);
    }

    public Long selectCount(Entity entity) {
        return this.selectCount((Entity) entity, (Integer) null);
    }

    public Long selectCount(Entity entity, Integer cacheSeconds) {
        String statementId = "EntitySQL.select";
        MappedStatement ms = this.sqlSession.getConfiguration().getMappedStatement(statementId);
        BoundSql originalBoundSql = this.getBoundSql(ms, entity);
        EntityBoundSql entityBoundSql = this.sqlGeneration.generateSelectSql(ms.getConfiguration(), entity);
        MetaObject boundSqlMeta = SystemMetaObject.forObject(originalBoundSql);
        boundSqlMeta.setValue("sql", entityBoundSql.getSql());
        boundSqlMeta.setValue("parameterMappings", entityBoundSql.getParameterMappings());
        return this.selectCount(statementId, originalBoundSql, entity, false, cacheSeconds);
    }

    public int insert(String statementId) {
        return this.insert(statementId, (Object) null);
    }

    public int insert(String statementId, Object param) {
        return this.sqlSession.insert(statementId, param);
    }

    public int insert(BaseEntity entity) {
        return this.updateEntity(SqlCommandType.INSERT, entity);
    }

    public void insertBatch(List<? extends BaseEntity> list) {
        this.executeBatch(SqlCommandType.INSERT, list, new String[0]);
    }

    public int update(String statementId) {
        return this.update(statementId, (Object) null);
    }

    public int update(String statementId, Object param) {
        return this.sqlSession.update(statementId, param);
    }

    public int update(BaseEntity entity) {
        return this.updateEntity(SqlCommandType.UPDATE, entity);
    }

    public void updateBatch(List<? extends BaseEntity> list, String... conditionColumns) {
        this.executeBatch(SqlCommandType.UPDATE, list, conditionColumns);
    }

    public int delete(String statementId) {
        return this.delete(statementId, (Object) null);
    }

    public int delete(String statementId, Object param) {
        return this.sqlSession.delete(statementId, param);
    }

    public int delete(Entity entity) {
        return this.updateEntity(SqlCommandType.DELETE, entity);
    }

    public void deleteBatch(List<? extends BaseEntity> list, String... conditionColumns) {
        this.executeBatch(SqlCommandType.DELETE, list, conditionColumns);
    }

    public int save(BaseEntity entity) {
        Entity data = (Entity) this.selectOne((Entity) entity);
        return data != null ? this.update(entity) : this.insert(entity);
    }

    public void executeBatch(String statementId, List<?> list) {
        if (list != null && list.size() != 0) {
            try {
                if (!ExecutorType.BATCH.equals(this.sqlSession.getConfiguration().getDefaultExecutorType())) {
                    BatchHolder.setBatch(new Batch(list.size()));
                }

                String updateStatementId = "EntitySQL.update";
                Iterator i$ = list.iterator();

                while (i$.hasNext()) {
                    Object object = i$.next();
                    MappedStatement originalMs = this.sqlSession.getConfiguration().getMappedStatement(statementId);
                    BoundSql originalBoundSql = this.getBoundSql(originalMs, object);
                    MappedStatement updateMs = this.sqlSession.getConfiguration().getMappedStatement(updateStatementId);
                    BoundSql updateBoundSql = this.getBoundSql(updateMs, object);
                    MetaObject boundSqlMeta = SystemMetaObject.forObject(updateBoundSql);
                    boundSqlMeta.setValue("sql", originalBoundSql.getSql());
                    boundSqlMeta.setValue("parameterMappings", originalBoundSql.getParameterMappings());
                    BoundSqlHolder.set(new CacheBoundSql(updateBoundSql, (Integer) null));
                    this.update(updateStatementId, object);
                    BoundSqlHolder.clear();
                }
            } finally {
                if (!ExecutorType.BATCH.equals(this.sqlSession.getConfiguration().getDefaultExecutorType())) {
                    BatchHolder.clear();
                }

                BoundSqlHolder.clear();
            }

        } else {
            throw new EmptyBatchListException();
        }
    }

    protected <E> PageData<E> selectList(String statementId, BoundSql originalBoundSql, Object param,
                                         Pagination pagination, Integer cacheSeconds) {
        PageData pageData = new PageData();

        PageData originalSql;
        try {
            BoundSqlHolder.set(new CacheBoundSql(originalBoundSql, cacheSeconds));
            Long count = Long.valueOf(0L);
            Properties properties = this.sqlSession.getConfiguration().getVariables();
            String maxLimitResultset = properties == null ? "" : properties.getProperty("maxLimitResultset", "");
            List originalBoundSqlMeta;
            if (pagination == null) {
                if ("".equals(maxLimitResultset)) {
                    originalBoundSqlMeta = this.selectList(statementId, param, RowBounds.DEFAULT);
                    pageData.setRows(originalBoundSqlMeta);
                    pageData.setTotal((long) originalBoundSqlMeta.size());
                    originalSql = pageData;
                    return originalSql;
                }

                pagination = new Pagination();
                pagination.setRange(0, Integer.parseInt(maxLimitResultset));
                pagination.setCurrPage(1);
                pagination.setNeedCount(false);
            } else if (!"".equals(maxLimitResultset) && pagination.getSize() > Integer.parseInt(maxLimitResultset)) {
                throw new ExceedsMaxLimitResultsetException(Integer.parseInt(maxLimitResultset), pagination.getSize());
            }

            if (!pagination.isBatch()) {
                if (pagination.isNeedCount()) {
                    count = this.selectCount(statementId, originalBoundSql, param, true, cacheSeconds);
                    if (count.longValue() == 0L) {
                        pageData.setRows(Collections.EMPTY_LIST);
                        pageData.setTotal(0L);
                        PageData originalBoundSqlMeta2 = pageData;
                        return originalBoundSqlMeta2;
                    }
                }

                MetaObject originalBoundSqlMeta1 = SystemMetaObject.forObject(originalBoundSql);
                String originalSql1 = originalBoundSql.getSql();
                boolean isPagination = false;
                if (!pagination.isNeedCount() || count.longValue() > (long) pagination.getSize()) {
                    isPagination = true;
                    String list = this.dialect.getPagingSql(originalSql1, pagination.getStart(),
                            pagination.getStart() + pagination.getSize());
                    originalBoundSqlMeta1.setValue("sql", list);
                }

                List list1 = this.selectList(statementId, param, new RowBounds(0, pagination.getSize()));
                pageData.setRows(list1);
                pageData.setTotal(count.longValue());
                pageData.setCurrPage(pagination.getCurrPage());
                pageData.setPageSize(pagination.getSize());
                if (isPagination) {
                    originalBoundSqlMeta1.setValue("sql", originalSql1);
                }

                return pageData;
            }

            originalBoundSqlMeta = this.export(statementId, param, this.exportPageSize);
            pageData.setRows(originalBoundSqlMeta);
            originalSql = pageData;
        } finally {
            BoundSqlHolder.clear();
        }

        return originalSql;
    }

    protected <E> List<E> selectList(String statementId, Object param, RowBounds rowBounds) {
        List list = null;

        try {
            MappedStatement ms = this.sqlSession.getConfiguration().getMappedStatement(statementId);
            if ("EntitySQL.select".equals(statementId) && param != null) {
                ResultMapUtil.resolveResultMapByEntity(ms, EntityHelper.getEntityClazz((Entity) param));
            } else {
                ResultMapUtil.resolveResultMap(ms);
            }

            list = this.sqlSession.selectList(statementId, param, rowBounds);
        } finally {
            ResultMapHolder.clear();
        }

        return list == null ? Collections.EMPTY_LIST : list;
    }

    protected Long selectCount(String statementId, BoundSql originalBoundSql, Object param, boolean isFromPagination,
                               Integer cacheSeconds) {
        Long count = Long.valueOf(0L);

        try {
            String originalSql = originalBoundSql.getSql();
            MetaObject originalBoundSqlMeta = SystemMetaObject.forObject(originalBoundSql);
            originalBoundSqlMeta.setValue("sql", this.dialect.getCountSql(originalSql));
            ResultMapUtil.resolveResultMapCount(this.sqlSession.getConfiguration().getMappedStatement(statementId));
            if (!isFromPagination) {
                BoundSqlHolder.set(new CacheBoundSql(originalBoundSql, cacheSeconds));
            }

            List list = this.selectList(statementId, param, RowBounds.DEFAULT);
            count = (Long) list.get(0);
            if (isFromPagination) {
                originalBoundSqlMeta.setValue("sql", originalSql);
            }
        } finally {
            if (!isFromPagination) {
                BoundSqlHolder.clear();
            }

        }

        return Long.valueOf(count == null ? 0L : count.longValue());
    }

    protected void executeBatch(SqlCommandType sqlCommandType, List<? extends BaseEntity> list,
                                String... conditionColumns) {
        if (list != null && list.size() != 0) {
            try {
                if (!ExecutorType.BATCH.equals(this.sqlSession.getConfiguration().getDefaultExecutorType())) {
                    BatchHolder.setBatch(new Batch(list.size()));
                }

                Iterator i$ = list.iterator();

                while (i$.hasNext()) {
                    BaseEntity entity = (BaseEntity) i$.next();
                    EntityUtil.handleOperColumn(entity, conditionColumns);
                    this.updateEntity(sqlCommandType, entity);
                }
            } finally {
                if (!ExecutorType.BATCH.equals(this.sqlSession.getConfiguration().getDefaultExecutorType())) {
                    BatchHolder.clear();
                }

            }

        } else {
            throw new EmptyBatchListException();
        }
    }

    protected int updateEntity(SqlCommandType sqlCommandType, Entity entity) {
        String statementId = "EntitySQL.update";
        MappedStatement ms = this.sqlSession.getConfiguration().getMappedStatement(statementId);
        BoundSql originalBoundSql = this.getBoundSql(ms, entity);
        EntityBoundSql entityBoundSql = null;
        if (SqlCommandType.INSERT.equals(sqlCommandType)) {
            entityBoundSql = this.sqlGeneration.generateInsertSql(ms.getConfiguration(), entity);
        } else if (SqlCommandType.UPDATE.equals(sqlCommandType)) {
            entityBoundSql = this.sqlGeneration.generateUpdateSql(ms.getConfiguration(), entity);
        } else if (SqlCommandType.DELETE.equals(sqlCommandType)) {
            entityBoundSql = this.sqlGeneration.generateDeleteSql(ms.getConfiguration(), entity);
        }

        MetaObject boundSqlMeta = SystemMetaObject.forObject(originalBoundSql);
        boundSqlMeta.setValue("sql", entityBoundSql.getSql());
        boundSqlMeta.setValue("parameterMappings", entityBoundSql.getParameterMappings());
        boolean rows = false;

        int rows1;
        try {
            BoundSqlHolder.set(new CacheBoundSql(originalBoundSql, (Integer) null));
            if (entity instanceof BaseEntity) {
                rows1 = this.update(statementId, entity);
            } else {
                rows1 = this.update(statementId);
            }
        } finally {
            BoundSqlHolder.clear();
        }

        return rows1;
    }

    private BoundSql getBoundSql(MappedStatement ms, Object parameterObject) {
        return ms.getBoundSql(this.wrapCollection(parameterObject));
    }

    private Object wrapCollection(Object object) {
        StrictMap map;
        if (object instanceof Collection) {
            map = new StrictMap();
            map.put("collection", object);
            if (object instanceof List) {
                map.put("list", object);
            }

            return map;
        } else if (object != null && object.getClass().isArray()) {
            map = new StrictMap();
            map.put("array", object);
            return map;
        } else {
            return object;
        }
    }
}